

#include "Parser.h"

#define INPUT_BUFFER_SIZE 50
/*  Command codes
-1 = bad input
0 = variable assignment/emptystring
1 = single command
2 = pipeline command
3 = for loop -- Not Used
4 = end for loop -- Not Used
5 = Conditionals
6 = Redirected streams

*/

using namespace std;

Parser::Parser(Environment *e){
    backGroundProcess = false;
    conditionals.clear();
    enviro = e;
    _stdIN="";
    _stdOUT="";
    _stdERR="";
}

int Parser::parseInput(const char* in){
    string input = string(in);


    if(input.compare("\n")==0){
        return commandCode = NOOUTPUT;
    }

    input = input.substr(0, input.size()-1);

    //Changes every instance of shell variables and replaces them before parsing commands
    int getVars = input.find_first_of('$', 0);
    while(getVars != -1){

        int length = 0;
        char counter = input.at(getVars+1);
        while(isalnum(counter)){

            if(getVars+1+length < input.size())
                counter = input.at(getVars+1+length);
            else{
                counter = '!';
                length--;
            }

            length ++;
        }

        string variable = input.substr(getVars+1, length);


        input.replace(getVars, length+1, (*enviro).getValue(variable));


        getVars = input.find_first_of('$', 0);

    }

    //Finds all instances of #D: and appends the full path when appropriate
    int getData = input.find("#D:", 0);
    while(getData != -1){
        int length = 0;
        char counter = input.at(getData+3);

        while(isalnum(counter)){

            if(getData+3+length < input.size())
                counter = input.at(getData+3+length);
            else{
                counter = '!';
                length--;
            }

            length ++;
        }

        string variable = input.substr(getData+3, length);

        (*enviro).getDataPath(&variable);
        input.replace(getData, length+3, variable);

        getData = input.find("#D:", 0);

    }


    /*
    If a '=' is present, it will assign the value to the designated variable.
    */
    int assignment = input.find_first_of('=', 0);
    if(assignment!=-1){

        int space = input.find_first_of(' ', 0);
        if(space == -1){                    //If there are no spaces then proceed with assigning the value to the variable
            string variable = input.substr(0, assignment);


            string value = input.substr(assignment + 1, string::npos);


            (*enviro).setValue(variable, value);


            return commandCode = NOOUTPUT;
        }


    }

    /*
    Looks for conditional arguments and parse it accordingly
    */
    int notConditional = input.find_first_of('!', 0);
    int andConditional = input.find("&&", 0);
    int orConditional = input.find("||", 0);

    if(notConditional!=-1 || andConditional!=-1 || orConditional!=-1){
        int index = 0;
        int length = 0;
        char currentChar = input[0];
        while(currentChar!='\0' && index!=-1 && index < input.size()){

            if(currentChar=='!'){
                conditionals.push_back('!');
                index++;
            }

            if( (andConditional==-1 || andConditional>orConditional) && orConditional!=-1){
                length = orConditional-index;
                conditionals.push_back('|');
            }
            else if( (orConditional==-1 || orConditional>andConditional) && andConditional!=-1){
                length = andConditional-index;
                conditionals.push_back('&');
            }
            else
                length = input.size();

            string singleCommand = input.substr(index, length);
            removeWhitespace(&singleCommand);

            processCommand(singleCommand);


            index = index+length+2;
            index = input.find_first_not_of(' ', index);

            if(index==-1)
                break;
            else
                currentChar = input[index];

            notConditional = input.find_first_of('!', index);
            andConditional = input.find("&&", index);
            orConditional = input.find("||", index);



        }
        return commandCode = CONDITIONAL;
    }


    /*
    If a '|' is present, it will try to parse it as pipeline commands
    */
    int pipeline =  input.find_first_of('|', 0);
    if(pipeline!=-1){
        string singleCommand = input.substr(0, pipeline);
        int index = 0;
        bool processPipeline = true;
        while(processPipeline){

            processCommand(singleCommand);
            index += pipeline + 1;
            pipeline = input.find_first_of('|', index);


            if(pipeline == -1){
                singleCommand = input.substr(index, input.size());
                processCommand(singleCommand);
                processPipeline = false;
            }
            else{
                pipeline -= index;
                singleCommand = input.substr(index, pipeline);
            }
        }
        return commandCode = COMMAND;
    }

    /*
    Handles any redirection of input/outputs
    */
    int redirectIn = input.find_first_of('<', 0);
    int redirectOut = input.find_first_of('>', 0);
    int redirectErr = input.find("2>", 0);
    bool hasRedirect = false;
    //Looks for redirection for stdin
    if(redirectIn!=-1){
        hasRedirect = true;
        if(redirectErr > redirectIn && (redirectOut==-1 || redirectErr < redirectOut)){
            _stdIN = input.substr(redirectIn+1, redirectErr-redirectIn-1);
        }
        else if(redirectOut > redirectIn && (redirectErr==-1 || redirectErr > redirectOut)){
            _stdIN = input.substr(redirectIn+1, redirectOut-redirectIn-1);
        }
        else{
            _stdIN = input.substr(redirectIn+1, input.size());
        }

    }
    //Looks for redirection for stdout
    if(redirectOut!=-1){
        hasRedirect = true;
        if(redirectErr > redirectOut && (redirectIn==-1 || redirectErr < redirectIn)){
            _stdOUT = input.substr(redirectOut+1, redirectErr-redirectOut-1);
        }
        else if(redirectIn > redirectOut && (redirectErr==-1 || redirectErr > redirectIn)){
            _stdOUT = input.substr(redirectOut+1, redirectIn-redirectOut-1);
        }
        else{
            _stdOUT = input.substr(redirectOut+1, input.size());
        }

    }
    //Looks for redirection for stderr
    if(redirectErr!=-1){
        hasRedirect = true;
        if(redirectIn > redirectErr && (redirectOut==-1 || redirectIn < redirectOut)){
            _stdERR = input.substr(redirectErr+2, redirectIn-redirectErr-2);
        }
        else if(redirectOut > redirectErr && (redirectIn==-1 || redirectIn > redirectOut)){
            _stdERR = input.substr(redirectErr+2, redirectOut-redirectErr-2);
        }
        else{
            _stdERR = input.substr(redirectErr+2, input.size());
        }
    }

    if(hasRedirect){
        //This is to strip out the command from the redirects
        int lastIndex = redirectErr;
        if(lastIndex == -1 || (redirectOut < lastIndex && redirectOut >=0)){
            lastIndex = redirectOut;
        }
        if(lastIndex == -1 || (redirectIn < lastIndex && redirectIn >=0)){
            lastIndex = redirectIn;
        }
        //Strips out leading white spaces of stdin, stdout, stderr
        removeWhitespace(&_stdERR);
        removeWhitespace(&_stdOUT);
        removeWhitespace(&_stdIN);
        string command = input.substr(0, lastIndex);
        removeWhitespace(&command);
        processCommand(command);
        return commandCode = COMMAND;
    }


    /*
    A single command is executed here.
    */

    processCommand(input);

    return commandCode = COMMAND;


}


/*
Parses a single command and stores it at the end of the list "commands".
@ pre: command - A single command input
@ return: void
*/
void Parser::processCommand(string command){
    removeWhitespace(&command);
    int space = command.find_first_of(' ', 0);
    string com;
    string args;
    if(space!=-1){
        com = command.substr(0, space);
        (*enviro).getCmdPath(&com);
        args = command.substr(space+1, command.size());
    }
    else {
        com = command.substr(0, command.size());
        (*enviro).getCmdPath(&com);
        args = "";
    }

    removeWhitespace(&args);
    if(command.size() > 0 && command.at(command.size()-1)=='&'){
        args = args.substr(0, args.size()-1);
        removeWhitespace(&args);
        backGroundProcess = true;
    }
    commandList.push_back(Command(com, args));
}


/*
Removes leading and trailing white space of any string.
*/
void Parser::removeWhitespace(string *str){
    int first = (*str).find_first_not_of(' ', 0);
    int last = (*str).find_last_not_of(' ', (*str).npos);
    if(first==-1)
        first = 0;

    *str = (*str).substr(first, last+1-first);

}

void Parser::reset(){
    commandList.clear();
    conditionals.clear();
    _stdERR.clear();
    _stdIN.clear();
    _stdOUT.clear();
    commandCode = -1;
    backGroundProcess = false;
}


char*** Parser::getArgList(){
    char*** commands = (char***) malloc((commandList.size()+1) * sizeof(char**));

    commands[commandList.size()] = NULL;

    int i = 0;
    list<Command>::iterator it = commandList.begin();
    while(it!=commandList.end()){
        commands[i] = (*it).getArgs();
        it++;
        i++;
    }
    return commands;
}

std::list<Command> Parser::getCmdList(){
    return commandList;
}



